package de.wiroo.setup.web.common;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.apache.wicket.extensions.markup.html.repeater.data.sort.ISortState;
import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider;
import org.apache.wicket.model.IModel;

import de.stefanocke.roo.wicket.RooStereotype;

/**
 * 
 * 
 * @author stefan
 *
 * @param <T> the entity type
 */
@RooStereotype("wicket.PersistenceBindings")
public abstract class ServiceBindingsBase <T, ID extends Serializable> implements IServiceBindings<T, ID> {
	protected class DataProviderBase_ extends DataProviderBase<T> {
		@Override
		public Iterator<? extends T> iterator(long first, long count) {
			//TODO: consider SortState
			return ServiceBindingsBase.this.iterator(first, count);
		}

		@Override
		public long size() {
			return ServiceBindingsBase.this.size();
		}

		@Override
		public IModel<T> model(T object) {
			return createModel(object);
		}

		@Override
		public void detach() {
			return;
		}
	}

	protected class EntityModel_ extends EntityModel<T, ID> {
		private EntityModel_(ID id, T entity) {
			super(id, entity);
		}

		@Override
		protected T load(ID id) {
			return ServiceBindingsBase.this.load(id);
		}
		
		@Override
		protected ID getId(T entity) {
			return ServiceBindingsBase.this.getId(entity);
		}
	}
	

	/* (non-Javadoc)
	 * @see de.wiroo.setup.web.common.IServiceBindings#createDataProvider()
	 */
	@Override
	public ISortableDataProvider<T, String> createDataProvider(){
		return new DataProviderBase_();
	}

	

	/* (non-Javadoc)
	 * @see de.wiroo.setup.web.common.IServiceBindings#createModel(T)
	 */
	@Override
	public IModel<T> createModel(T object) {
		return new EntityModel_(null, object);
	}
	
	/* (non-Javadoc)
	 * @see de.wiroo.setup.web.common.IServiceBindings#createModelById(ID)
	 */
	@Override
	public IModel<T> createModelById(ID id) {
		return new EntityModel_(id, null);
	}

	/**
	 * The total number of entity instances in the given context.
	 * <p>
	 * TODO: Filter params.
	 * 
	 * @return
	 */
	protected abstract long size();

	/**
	 * TODO: Filter params.
	 * 
	 * @param first
	 * @param count
	 * @return
	 */
	protected abstract Iterator<? extends T> iterator(long first, long count);
	
	/**
	 * TODO: fetch params to determine what to load?
	 * <p>
	 * Loads an entity by its ID.
	 * @param id
	 * @return the entity
	 */
	protected abstract T load(ID id);
	
	protected abstract ID getId(T entity);
	
	
	
	
	//TODO: Make lazy?
	private IAction<IModel<T>, Class<T>> createAction = new IAction<IModel<T>, Class<T>>() {
		@Override
		public String getActionId() {
			return ACTION_ID_CREATE;
		}
		
		@Override
		public boolean isEnabled(Class<T> objectClass) {
			return isCreateEnabled(); //TODO: Use param?
		}
		@Override
		public IModel<T> execute(Class<T> objectClass) {
			return createModel(null);  //TODO: Use param to create subclasses? 
			//TODO: createNewModel ...
		}

		@Override
		public boolean needsConfirmation() {
			return false;
		}
		
	};
	
	private IAction<IModel<T>, IModel<T>> updateAction = new IAction<IModel<T>, IModel<T>>() {
		@Override
		public String getActionId() {
			return ACTION_ID_UPDATE;
		}
		@Override
		public boolean isEnabled(IModel<T> model) {
			return isUpdateEnabled(model.getObject());
		}
		@Override
		public IModel<T> execute(IModel<T> model) {
			return model; //Just pass the model through to the form ...
		}
		@Override
		public boolean needsConfirmation() {
			return false;
		};
	};
	
	private IAction<Void, IModel<T>> deleteAction = new  IAction<Void, IModel<T>>() {
		@Override
		public String getActionId() {
			return ACTION_ID_DELETE;
		}
		@Override
		public boolean isEnabled(IModel<T> model) {
			return isDeleteEnabled(model.getObject());
		};
		
		@Override
		public Void execute(IModel<T> model) {
			delete(model.getObject());
			return null;
		}
		
		public boolean needsConfirmation() {
			return true;
		};
	};
	
	private IAction<IModel<T>, IModel<T>> saveAction = new IAction<IModel<T>, IModel<T>>() {
		@Override
		public String getActionId() {
			return ACTION_ID_SAVE;
		}
		
		@Override
		public IModel<T> execute(IModel<T> model) {
			T entity = model.getObject();
			T persistedEntity = save(entity);
			if(entity != persistedEntity){
				return createModel(persistedEntity);
			} else {
				return model;
			}
		}

		@Override
		public boolean isEnabled(IModel<T> param) {
			return true;
		}

		@Override
		public boolean needsConfirmation() {
			return false;
		}
	};
	
	private List<IAction<?, IModel<T>>> singleEntityActions = null;
	
	/* (non-Javadoc)
	 * @see de.wiroo.setup.web.common.IServiceBindings#getSingleEntityActions()
	 */
	@Override
	public List<IAction<?, IModel<T>>> getSingleEntityActions(){
		if(singleEntityActions==null){
			singleEntityActions = Collections.unmodifiableList(createSingleEntityActions());
		}
		return singleEntityActions;
	}
	
	protected List<IAction<?, IModel<T>>> createSingleEntityActions() {
		List<IAction<?, IModel<T>>> list = new ArrayList<IAction<?, IModel<T>>>();
		list.add(updateAction);
		list.add(deleteAction);
		return list;
	}
	
	private List<IAction<IModel<T>, Class<T>>> createEntityActions = null;
	
	/* (non-Javadoc)
	 * @see de.wiroo.setup.web.common.IServiceBindings#getCreateActions()
	 */
	@Override
	public List<IAction<IModel<T>, Class<T>>> getCreateActions() {
		if(createEntityActions==null){
			createEntityActions = Collections.unmodifiableList(createCreateEntityActions());
		}
		return createEntityActions;
	}
	

	protected List<IAction<IModel<T>, Class<T>>> createCreateEntityActions() {
		return Collections.singletonList(createAction);
	}
	
	/* (non-Javadoc)
	 * @see de.wiroo.setup.web.common.IServiceBindings#getSaveActions()
	 */
	@Override
	public List<IAction<IModel<T>, IModel<T>>> getSaveActions() {
		//TODO: Make lazy
		List<IAction<IModel<T>, IModel<T>>> list = Collections.singletonList(saveAction);
		return Collections.unmodifiableList(list);
	}

	/**
	 * 
	 * @return true means, it is allowed to create new instances of the entity in the current context
	 */
	protected boolean isCreateEnabled(){
		return true;
	}
	
	/**
	 * 
	 * @param entity the entity to be updated
	 * @return true means, it is allowed to update the entity in the current context.
	 */
	protected boolean isUpdateEnabled(T entity){
		return true;
	}
	
	/**
	 * Persists the changes made to the entity. For create and update.
	 * @return 
	 */
	protected abstract T save(T entity);

	/**
	 * 
	 * @param entity the entity to be deleted
	 * @return true means, it is allowed to update the entity in the current context.
	 */
	protected boolean isDeleteEnabled(T entity){
		return true;
	}
	
	/**
	 * Deletes the entity.
	 * 
	 * @param entity the entity to be deleted
	 */
	protected abstract void delete(T entity);
	
		
}
